//
//  Problem1760.swift
//  TestProject
//
//  Created by 武侠 on 2021/5/24.
//  Copyright © 2021 zhulong. All rights reserved.
//

import UIKit

/*
 1760. 袋子里最少数目的球
 给你一个整数数组 nums ，其中 nums[i] 表示第 i 个袋子里球的数目。同时给你一个整数 maxOperations 。

 你可以进行如下操作至多 maxOperations 次：

 选择任意一个袋子，并将袋子里的球分到 2 个新的袋子中，每个袋子里都有 正整数 个球。
 比方说，一个袋子里有 5 个球，你可以把它们分到两个新袋子里，分别有 1 个和 4 个球，或者分别有 2 个和 3 个球。
 你的开销是单个袋子里球数目的 最大值 ，你想要 最小化 开销。
 请你返回进行上述操作后的最小开销。

 示例 1：
     输入：nums = [9], maxOperations = 2
     输出：3
     解释：
     - 将装有 9 个球的袋子分成装有 6 个和 3 个球的袋子。[9] -> [6,3] 。
     - 将装有 6 个球的袋子分成装有 3 个和 3 个球的袋子。[6,3] -> [3,3,3] 。
     装有最多球的袋子里装有 3 个球，所以开销为 3 并返回 3 。
 示例 2：
     输入：nums = [2,4,8,2], maxOperations = 4
     输出：2
     解释：
     - 将装有 8 个球的袋子分成装有 4 个和 4 个球的袋子。[2,4,8,2] -> [2,4,4,4,2] 。
     - 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,4,4,4,2] -> [2,2,2,4,4,2] 。
     - 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,4,4,2] -> [2,2,2,2,2,4,2] 。
     - 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,2,2,4,2] -> [2,2,2,2,2,2,2,2] 。
     装有最多球的袋子里装有 2 个球，所以开销为 2 并返回 2 。
 示例 3：
     输入：nums = [7,17], maxOperations = 2
     输出：7
 提示：
     1 <= nums.length <= 10的5次方
     1 <= maxOperations, nums[i] <= 10的9次方
 */
@objcMembers class Problem1760: NSObject {
    func solution() {
        print(minimumSize([9], 2))
        print(minimumSize([2,4,8,2], 4))
        print(minimumSize([7,17], 2))
    }
    
    /*
     二分法
     我们来确定一下left和right：
     1: left  肯定是1（因为：1 <= maxOperations, nums[i]）
     2: right 肯定是数组中的最大值（因为如果极端一点：不执行切割，那么就是最大值）
     现在就变成了：看看如果最大值是mid时，需要花费多少次，看看次数和给定的maxOperations进行比较
     1: 遍历数组：如果当前值nums[i] <= mid，不需要花费次数
     2: nums[i] > mid, 花费的次数就是 = nums[i] / mid ，例如 10/2 需要切割5次
     3: 遍历完数组，如果次数sum <= maxOperations, 说明sum不满足需求, right = mid - 1
     4: sum > maxOperations, 说明我们可以有机会花费更大的次数，让sum更小, left = mid + 1
     
     */
    func minimumSize(_ nums: [Int], _ maxOperations: Int) -> Int {
        var l = 1, r = nums.max()!
        var mid = 0
        
        var minValue = r
        while l <= r {
            mid = l + (r - l) >> 1
            // 看看转换成mid，需要花费的次数
            var n = 0
            for num in nums {
                n += (num - 1) / mid     // -1的目的是 ：如果num 与 mid 相等，不需要转换
            }
            
            if n <= maxOperations { // 满足需求，我看看是否可以让mid更小
                r = mid - 1
                minValue = min(minValue, mid)   // mid 满足
            } else {
                l = mid + 1
            }
        }
        
        return minValue
    }
}
